package reader;

import exception.UnsupportedTagException;

import java.util.ArrayList;

/**
* Created by Pavel Nikolaev
*/
public class HtmlTag {
    public enum TagType {
        HTML,
        HEAD,
        TITLE,
        BODY,
        B,
        I,
        H1,
        H2,
        H3,
        H4,
        H5,
        H6,
        A,
        P,
        TABLE,
        TR,
        TD,
        IMG,
        BR,
        HR,
        UL,
        OL,
        LI,
        PRE,
        FAKE_TAG
    }

    public TagType type = null;
    public String name = "";
    public boolean isOpen = true;
    public boolean hasAttribute = false;
    public ArrayList<HtmlTagAttribute> attributes = null;

    public HtmlTag(String raw) throws UnsupportedTagException {
        type = null;
        isOpen = true;
        hasAttribute = false;
        attributes = null;
        if (!parse(raw)) throw new UnsupportedTagException(raw);
    }

    public HtmlTag(TagType certainType) {
        type = certainType;
        name = "";
        isOpen = true;
        hasAttribute = false;
        attributes = null;
    }

    private enum TagParserState {
        TAG_START,
        TAG_OPEN_FLAG,
        TAG_NAME,
        TAG_WAIT_ATTRIBUTE_NAME,
        TAG_ATTRIBUTE_NAME,
        TAG_WAIT_ATTRIBUTE_VALUE,
        TAG_ATTRIBUTE_VALUE,
        TAG_WAIT_END,
        TAG_ERROR,
        TAG_SUCCESS
    }

    public boolean parse(String raw)  throws UnsupportedTagException {
        TagParserState state = TagParserState.TAG_START;
        int startOffset = 0;
        int currentOffset = 0;
        while ((currentOffset < raw.length()) &&
                (state != TagParserState.TAG_ERROR) &&
                (state != TagParserState.TAG_SUCCESS)) {
            final char ch = raw.charAt(currentOffset);
            switch(state) {
                case TAG_START:
                    if (ch == '<') state = TagParserState.TAG_OPEN_FLAG;
                    break;
                case TAG_OPEN_FLAG:
                    if (ch == '/') {
                        isOpen = false;
                        startOffset = currentOffset + 1;
                    } else {
                        startOffset = currentOffset;
                    }
                    state = TagParserState.TAG_NAME;
                    break;
                case TAG_NAME:
                    if (ch == '>') {
                        setType(raw.substring(startOffset, currentOffset));
                        if (type == null) state = TagParserState.TAG_ERROR;
                        else state = TagParserState.TAG_SUCCESS;
                    } else if (ch == '/') {
                        setType(raw.substring(startOffset, currentOffset));
                        if (type == null) state = TagParserState.TAG_ERROR;
                        else state = TagParserState.TAG_WAIT_END;
                    } else if (Character.isSpaceChar(ch)) {
                        setType(raw.substring(startOffset, currentOffset));
                        if (type == null) state = TagParserState.TAG_ERROR;
                        else state = TagParserState.TAG_WAIT_ATTRIBUTE_NAME;
                    }
                    break;
                case TAG_WAIT_ATTRIBUTE_NAME:
                    if (ch == '>') {
                        state = TagParserState.TAG_SUCCESS;
                    } else if (!Character.isSpaceChar(ch)) {
                        startOffset = currentOffset;
                        state = TagParserState.TAG_ATTRIBUTE_NAME;
                    }
                    break;
                case TAG_ATTRIBUTE_NAME:
                    if (ch == '>') {
                        pushAttribute(raw.substring(startOffset, currentOffset));
                        state = TagParserState.TAG_SUCCESS;
                    } else if (ch == '=') {
                        pushAttribute(raw.substring(startOffset, currentOffset));
                        state = TagParserState.TAG_WAIT_ATTRIBUTE_VALUE;
                    }
                    break;
                case TAG_WAIT_ATTRIBUTE_VALUE:
                    if (ch == '>') {
                        state = TagParserState.TAG_ERROR;
                    } else if (ch == '"') {
                        startOffset = currentOffset + 1;
                        state = TagParserState.TAG_ATTRIBUTE_VALUE;
                    }
                    break;
                case TAG_ATTRIBUTE_VALUE:
                    if (ch == '>') {
                       state = TagParserState.TAG_ERROR;
                    } else if (ch == '"') {
                       attributes.get(attributes.size() - 1).value = raw.substring(startOffset, currentOffset);
                       state = TagParserState.TAG_WAIT_END;
                    }
                    break;
                case TAG_WAIT_END:
                    if (Character.isSpaceChar(ch)) state = TagParserState.TAG_WAIT_ATTRIBUTE_NAME;
                    else if (ch == '>') state = TagParserState.TAG_SUCCESS;
                    else state = TagParserState.TAG_ERROR;
                    break;
            }
            ++currentOffset;
        }
        return (state == HtmlTag.TagParserState.TAG_SUCCESS);
    }

    private void pushAttribute(String name) {
        if (!hasAttribute) {
            attributes = new ArrayList<HtmlTagAttribute>();
            hasAttribute = true;
        }
        HtmlTagAttribute att = new HtmlTagAttribute();
        att.name = name.toUpperCase();
        attributes.add(att);
    }

    private void setType(String rawName) {
        try {
            type = TagType.valueOf(rawName.toUpperCase());
        } catch (RuntimeException ignored) {
            type = null;
        }
        name = rawName;
    }
}
